/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package tools;

import com.openbrew.openbrewrf.AdditiveNotes;
import com.openbrew.openbrewrf.Administration;
import com.openbrew.openbrewrf.Beer;
import com.openbrew.openbrewrf.BoilHopNotes;
import com.openbrew.openbrewrf.BoilHops;
import com.openbrew.openbrewrf.BrewLab;
import com.openbrew.openbrewrf.BrewLabDetails;
import com.openbrew.openbrewrf.BrewNotes;
import com.openbrew.openbrewrf.Brews;
import com.openbrew.openbrewrf.DryHopNotes;
import com.openbrew.openbrewrf.DryHops;
import com.openbrew.openbrewrf.Fv;
import com.openbrew.openbrewrf.Hops;
import com.openbrew.openbrewrf.LabType;
import com.openbrew.openbrewrf.Malt;
import com.openbrew.openbrewrf.MaltBill;
import com.openbrew.openbrewrf.MaltNotes;
import com.openbrew.openbrewrf.MashNotes;
import com.openbrew.openbrewrf.MashStep;
import com.openbrew.openbrewrf.PitchNotes;
import com.openbrew.openbrewrf.RecipeAdditives;
import com.openbrew.openbrewrf.Recipes;
import com.openbrew.openbrewrf.SaltNotes;
import com.openbrew.openbrewrf.SelectedSalt;
import com.openbrew.openbrewrf.Supplies;
import com.openbrew.openbrewrf.views.BeerView;
import com.openbrew.openbrewrf.views.BrewView;
import java.awt.Color;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.swing.JOptionPane;
import service.enumerations.LabResult;
import service.enumerations.MovementState;
import service.enumerations.Units;

/**
 *
 * @author chapmand
 */
public class BrewsTools {
    private final DateFormat timeFormat = new SimpleDateFormat("HH:mm");
    
    /*********************************************************************
     * Do a volume removal from brew.
     * @param en
     * @param volume
     * @param brewID 
     */
    public void doVolumeRemoval(final EntityManager en, final float volume
            , final int brewID){
        Brews brew = getBrewFromID(en, brewID);
        final float newVolume = brew.getVolLeft() - volume;
        brew.setVolLeft(newVolume);
        if(newVolume < 0.9){
            brew.setBState(MovementState.KEGGED.getState());
        }
        en.merge(brew);
    }

    /**
     * Updates the tank volume when a keg movement is done.
     * @param en - Entity Manager
     * @param volume - Volume of beer being moved.
     * @param batchID - ID of the batch/brew being modified.
     */
    public void updateTankVolumeForKegMovement(final EntityManager en
            , final double volume, final int batchID){
        Brews updatedBatch = getBrewFromID(en, batchID);
        double newVolume = updatedBatch.getVolLeft() - volume;
        if(newVolume < 0) {
            newVolume=0;
        }
        updatedBatch.setVolLeft((float)newVolume);
        //update state if volume is low enough.
        if(newVolume == 0){
            updatedBatch.setBState(MovementState.KEGGED.getState());
            final java.util.Date date = new java.util.Date();
            updatedBatch.setPDate(date);
        }
        en.merge(updatedBatch);
    }

    /**
     * Updates the tank volume when a bottle movement is done.
     * @param en - Entity Manager
     * @param volume - Volume of beer being moved.
     * @param batchID - ID of the batch/brew being modified.
     */
    public void updateTankVolumeForBottleMovement(final EntityManager en
            , final double volume, final int batchID){
        Brews updatedBatch = getBrewFromID(en, batchID);
        double newVolume = updatedBatch.getVolLeft() - volume;
        if(newVolume < 0) {
            newVolume=0;
        }
        updatedBatch.setVolLeft((float)newVolume);
        //update state if volume is low enough.
        if(newVolume == 0){
            updatedBatch.setBState(MovementState.BOTTLED.getState());
            final java.util.Date date = new java.util.Date();
            updatedBatch.setPDate(date);
        }
        en.merge(updatedBatch);
    }
    
    /***************************************************************************
     * Do an update to brew based on before and after update.
     * @param en - Entity Manager
     * @param newBrew - Brew to update.
     **************************************************************************/
    public void doBrewUpdate(final EntityManager en, final Brews newBrew){
        //Update all brew notes if they aren't null.
        final InventoryModifiers im = new InventoryModifiers();
        float finalVolume = newBrew.getVolLeft();
        if(newBrew.getBrewNoteID()!=null){
            finalVolume += checkForVolumeAddition(en, newBrew.getBrewNoteID());
            doBrewNoteUpdate(en, newBrew.getBrewNoteID(), newBrew.getRid(), im);
        }
        if(newBrew.getBrewNoteID2()!=null){
            finalVolume += checkForVolumeAddition(en, newBrew.getBrewNoteID2());
            doBrewNoteUpdate(en, newBrew.getBrewNoteID2(), newBrew.getRid(), im);
        }
        if(newBrew.getBrewNoteID3()!=null){
            finalVolume += checkForVolumeAddition(en, newBrew.getBrewNoteID3());
            doBrewNoteUpdate(en, newBrew.getBrewNoteID3(), newBrew.getRid(), im);
        }
        if(newBrew.getBrewNoteID4()!=null){
            finalVolume += checkForVolumeAddition(en, newBrew.getBrewNoteID4());
            doBrewNoteUpdate(en, newBrew.getBrewNoteID4(), newBrew.getRid(), im);
        }
        if(newBrew.getBrewNoteID5()!=null){
            finalVolume += checkForVolumeAddition(en, newBrew.getBrewNoteID5());
            doBrewNoteUpdate(en, newBrew.getBrewNoteID5(), newBrew.getRid(), im);
        }
        if(newBrew.getBrewNoteID6()!=null){
            finalVolume += checkForVolumeAddition(en, newBrew.getBrewNoteID6());
            doBrewNoteUpdate(en, newBrew.getBrewNoteID6(), newBrew.getRid(), im);
        }
        //Now check old brew to new brew.
        final Brews oldBrew = getBrewFromID(en, newBrew.getBid());
        checkForDryHopChange(en, newBrew, oldBrew, im);
        //Now update YeastGens if needed.
        checkForYeastGenChange(im, en, newBrew, oldBrew);
        
        newBrew.setVolLeft(finalVolume);
        //Finally update the brew.
        en.merge(newBrew);
    }
    
    private void checkForYeastGenChange(final InventoryModifiers im, final EntityManager en, final Brews newBrew, final Brews oldBrew){
        List<PitchNotes> oldPitchNotes = new ArrayList<PitchNotes>();
        List<PitchNotes> newPitchNotes = new ArrayList<PitchNotes>();
        if(oldBrew.getPitchNoteCollection()!=null){
            oldPitchNotes = oldBrew.getPitchNoteCollection();
        }
        if(newBrew.getPitchNoteCollection()!=null){
            newPitchNotes = newBrew.getPitchNoteCollection();
        }
        if(oldPitchNotes.size() <= newPitchNotes.size()){
            int idx = 0;
            if(newBrew.getPitchNoteCollection()!=null){
                for(final PitchNotes pn : newBrew.getPitchNoteCollection()){
                    if(pn.getYeastGenId()!=null){
                        if(oldBrew.getPitchNoteCollection() != null 
                                && oldBrew.getPitchNoteCollection().size() > idx){
                            final PitchNotes oldPn = oldBrew.getPitchNoteCollection().get(idx);
                            doYeastGenCheck(im, en, pn.getYeastGenId().getYeastGenID(), pn.getVolPitched(), oldPn.getYeastGenId().getYeastGenID(), oldPn.getVolPitched());
                        } else {
                            doYeastGenCheck(im, en, pn.getYeastGenId().getYeastGenID(), pn.getVolPitched(), 0, 0);
                        }
                    }
                    idx++;
                }
            }
        } else {
            //One was removed we need to find it and credit it back.
            for(final PitchNotes pn : oldPitchNotes){
                if(pn.getYeastGenId() != null){
                    boolean found = false;
                    for(final PitchNotes newPn : newPitchNotes){
                        if(newPn.getPitchNoteID() == pn.getPitchNoteID()){
                            found = true;
                        }
                    }
                    if(!found){
                        doYeastGenCheck(im, en, 0, 0, pn.getYeastGenId().getYeastGenID(), pn.getVolPitched());
                    }
                }
            }
        }
    }
    
    private void doYeastGenCheck(final InventoryModifiers im, final EntityManager en
            , final int newYeastGenID, final float newVolume
            , final int oldYeastGenID, final float oldVolume){
        if(newYeastGenID != 0){
            if(oldYeastGenID != newYeastGenID && oldYeastGenID != 0){
                //Credit old yeast gen
                im.doYeastGenReturn(en, oldYeastGenID, oldVolume);
                //Deduct new yeast gen
                im.doYeastGenRemoval(en, newYeastGenID, newVolume);
            } else if(oldVolume != newVolume){
                if(oldVolume > newVolume){
                    //Return old yeast Gen.
                    im.doYeastGenReturn(en, oldYeastGenID, (oldVolume - newVolume));
                } else {
                    //Deduct new Yeast Gen.
                    im.doYeastGenRemoval(en, newYeastGenID, (newVolume - oldVolume));
                }
            }
        } else if(oldYeastGenID != 0 && newYeastGenID == 0){
            im.doYeastGenReturn(en, oldYeastGenID, oldVolume);
        }
    }
    
    private float checkForVolumeAddition(final EntityManager en, final BrewNotes newBrewNote){
        final BrewNotes oldBrewNote = getBrewNoteFromID(en, newBrewNote.getBnid());
        if(oldBrewNote.getVolFinal()==0 && newBrewNote.getVolFinal()>0){
            return newBrewNote.getVolFinal();
        }
        return 0;
    }
    
    private void checkForDryHopChange(final EntityManager en, final Brews newBrew
            , final Brews oldBrew, final InventoryModifiers im){
        List<DryHopNotes> oldDryHopNotes = new ArrayList<DryHopNotes>();
        List<DryHopNotes> newDryHopNotes = new ArrayList<DryHopNotes>();
        if(oldBrew.getDryHopNoteCollection()!=null){
            oldDryHopNotes = oldBrew.getDryHopNoteCollection();
        }
        if(newBrew.getDryHopNoteCollection()!=null){
            newDryHopNotes = newBrew.getDryHopNoteCollection();
        }
        if(oldDryHopNotes.size() <= newDryHopNotes.size()){
            int idx = 0;
            if(newBrew.getDryHopNoteCollection()!=null){
                for(final DryHopNotes dhn : newBrew.getDryHopNoteCollection()){
                    if(oldBrew.getDryHopNoteCollection() != null 
                            && oldBrew.getDryHopNoteCollection().size() > idx){
                        final DryHopNotes oldDhn = oldBrew.getDryHopNoteCollection().get(idx);
                        doHopChangeCheck(im, en, dhn.getHopID().getHid(), dhn.getDryHopLbs(), oldDhn.getHopID().getHid(), oldDhn.getDryHopLbs());
                    } else {
                        doHopChangeCheck(im, en, dhn.getHopID().getHid(), dhn.getDryHopLbs(), 0, 0);
                    }
                    idx++;
                }
            }
        } else {
            //One was removed we need to find it and credit it back.
            for(final DryHopNotes dhn : oldDryHopNotes){
                boolean found = false;
                for(final DryHopNotes newDhn : newDryHopNotes){
                    if(newDhn.getDryHopNoteID() == dhn.getDryHopNoteID()){
                        found = true;
                    }
                }
                if(!found){
                    doHopChangeCheck(im, en, 0, 0, dhn.getHopID().getHid(), dhn.getDryHopLbs());
                }
            }
        }
    }
    
    private void doBrewNoteUpdate(final EntityManager en, final BrewNotes newBrewNote,
            final Recipes recipe, InventoryModifiers im){
        final BrewNotes oldBrewNote = getBrewNoteFromID(en, newBrewNote.getBnid());
        //Modify supplies if any changes are found.
        checkForSupplyChange(en, newBrewNote, oldBrewNote, recipe, im);
        //Modify Water Salts
        checkForWaterSaltChange(en, newBrewNote, oldBrewNote, recipe, im);
        //Modify hops if any changes are found.
        checkForHopChange(en, newBrewNote, oldBrewNote, im);
        //Modify malt if any changes are found.
        checkForMaltChange(en, newBrewNote, oldBrewNote, im);
        //Update the mash notes.
        doMashNoteUpdate(en, newBrewNote);
        //update malt notes;
        if(newBrewNote.getMaltNoteCollection()!=null){
            List<MaltNotes> lines = new ArrayList<MaltNotes>();
            for(final MaltNotes mn : newBrewNote.getMaltNoteCollection()){
                if(mn.getMaltNoteID()==null || mn.getMaltNoteID()==0){
                    lines.add(createMaltNote(en, mn));
                } else {
                    en.merge(mn);
                    lines.add(mn);
                }
            }
            newBrewNote.setMaltNoteCollection(lines);
        }
        //update additive notes;
        if(newBrewNote.getAdditiveNoteCollection()!=null){
            List<AdditiveNotes> lines = new ArrayList<AdditiveNotes>();
            for(final AdditiveNotes an : newBrewNote.getAdditiveNoteCollection()){
                if(an.getAdditiveNoteID()==null || an.getAdditiveNoteID() == 0){
                    lines.add(createAdditiveNote(en, an));
                } else {
                    en.merge(an);
                    lines.add(an);
                }
            }
            newBrewNote.setAdditiveNoteCollection(lines);
        }
        //update Boil Hop notes;
        if(newBrewNote.getBoilHopNoteCollection()!=null){
            List<BoilHopNotes> lines = new ArrayList<BoilHopNotes>();
            for(final BoilHopNotes bhn : newBrewNote.getBoilHopNoteCollection()){
                if(bhn.getBoilHopNoteID() == null || bhn.getBoilHopNoteID() == 0){
                    lines.add(createBoilHopNote(en, bhn));
                } else {
                    en.merge(bhn);
                    lines.add(bhn);
                }
            }
            newBrewNote.setBoilHopNoteCollection(lines);
        }
        //update Salt notes;
        if(newBrewNote.getSaltNoteCollection()!=null){
            List<SaltNotes> lines = new ArrayList<SaltNotes>();
            for(final SaltNotes sn : newBrewNote.getSaltNoteCollection()){
                if(sn.getSaltNoteID() == null || sn.getSaltNoteID() == 0){
                    lines.add(createSaltNote(en, sn));
                } else {
                    en.merge(sn);
                    lines.add(sn);
                }
            }
            newBrewNote.setSaltNoteCollection(lines);
        }
        //Update the brew note.
        en.merge(newBrewNote);
    }
    
    private AdditiveNotes createAdditiveNote(final EntityManager en, final AdditiveNotes an){
        en.persist(an);
        en.flush();
        en.refresh(an);
        return an;
    }
    
    private BoilHopNotes createBoilHopNote(final EntityManager en, final BoilHopNotes bhn){
        en.persist(bhn);
        en.flush();
        en.refresh(bhn);
        return bhn;
    }
    
    private MaltNotes createMaltNote(final EntityManager en, final MaltNotes mn){
        en.persist(mn);
        en.flush();
        en.refresh(mn);
        return mn;
    }
    
    private SaltNotes createSaltNote(final EntityManager en, final SaltNotes sn){
        en.persist(sn);
        en.flush();
        en.refresh(sn);
        return sn;
    }
    
    private void doMashNoteUpdate(final EntityManager en, final BrewNotes newBrewNote){
        for(final MashNotes mashNote : newBrewNote.getMashNoteCollection()){
            en.merge(mashNote);
        }
    }
    
    /***************************************************************************
     * Malt Modifiers
     * @param en
     * @param newBrewNote
     * @param oldBrewNote
     * @param recipe
     * @param im 
     ***************************************************************************/
    private void checkForMaltChange(final EntityManager en, final BrewNotes newBrewNote
            , final BrewNotes oldBrewNote, final InventoryModifiers im){
        List<MaltNotes> oldMaltNotes = new ArrayList<MaltNotes>();
        List<MaltNotes> newMaltNotes = new ArrayList<MaltNotes>();
        if(oldBrewNote.getMaltNoteCollection()!=null){
            oldMaltNotes = oldBrewNote.getMaltNoteCollection();
        }
        if(newBrewNote.getMaltNoteCollection()!=null){
            newMaltNotes = newBrewNote.getMaltNoteCollection();
        }
        if(oldMaltNotes.size() <= newMaltNotes.size()){
            int idx = 0;
            if(newBrewNote.getMaltNoteCollection() != null){
                for(final MaltNotes mn : newBrewNote.getMaltNoteCollection()){
                    if(oldBrewNote.getMaltNoteCollection() != null
                            && oldBrewNote.getMaltNoteCollection().size() > idx){
                        doMaltChangeCheck(im, en, mn.getMaltNoteLbs(), oldBrewNote.getMaltNoteCollection().get(idx).getMaltNoteLbs(), mn.getMaltID());
                    } else {
                        doMaltChangeCheck(im, en, mn.getMaltNoteLbs(), 0, mn.getMaltID());
                    }
                    idx++;
                }
            }
        } else {
            //One was removed we need to find it and credit it back.
            for(final MaltNotes mn : oldMaltNotes){
                boolean found = false;
                for(final MaltNotes newMn : newMaltNotes){
                    if(newMn.getMaltNoteID() == mn.getMaltNoteID()){
                        found = true;
                    }
                }
                if(!found){
                    doMaltChangeCheck(im, en, 0, mn.getMaltNoteLbs(), mn.getMaltID());
                }
            }
        } 
    }
    
    private void doMaltChangeCheck(final InventoryModifiers im, final EntityManager en
            , final float newMaltQty, final float oldMaltQty, final Malt malt){
        if(malt != null){
            if(oldMaltQty > newMaltQty){
                //Return old malt.
                im.doMaltReturnWithAllocation(en, malt.getMaltID(), (oldMaltQty - newMaltQty));
            } else {
                //Deduct new malt.
                im.doMaltRemovalWithAllocation(en, malt.getMaltID(), (newMaltQty - oldMaltQty));
            }
        }
    }
    
    /***************************************************************************
     * Hop Modifiers
     * @param en
     * @param newBrewNote
     * @param oldBrewNote
     * @param im 
     ***************************************************************************/
    private void checkForHopChange(final EntityManager en, final BrewNotes newBrewNote
            , final BrewNotes oldBrewNote, final InventoryModifiers im){
        List<BoilHopNotes> oldHopNotes = new ArrayList<BoilHopNotes>();
        List<BoilHopNotes> newHopNotes = new ArrayList<BoilHopNotes>();
        if(oldBrewNote.getBoilHopNoteCollection()!=null){
            oldHopNotes = oldBrewNote.getBoilHopNoteCollection();
        }
        if(newBrewNote.getBoilHopNoteCollection()!=null){
            newHopNotes = newBrewNote.getBoilHopNoteCollection();
        }
        if(oldHopNotes.size() <= newHopNotes.size()){
            int idx = 0;
            if(newBrewNote.getBoilHopNoteCollection()!=null){
                for(final BoilHopNotes bhn : newBrewNote.getBoilHopNoteCollection()){
                    if(oldBrewNote.getBoilHopNoteCollection() != null
                            && oldBrewNote.getBoilHopNoteCollection().size() > idx){
                        final BoilHopNotes oldBhn = oldBrewNote.getBoilHopNoteCollection().get(idx);
                        doHopChangeCheck(im, en, bhn.getHopID().getHid(), bhn.getBoilHopLbs(), oldBhn.getHopID().getHid(), oldBhn.getBoilHopLbs());
                    } else {
                        doHopChangeCheck(im, en, bhn.getHopID().getHid(), bhn.getBoilHopLbs(), 0, 0);
                    }
                    idx++;
                }
            }
        } else {
            //One was removed we need to find it and credit it back.
            for(final BoilHopNotes bhn : oldHopNotes){
                boolean found = false;
                for(final BoilHopNotes newBhn : newHopNotes){
                    if(newBhn.getBoilHopNoteID() == bhn.getBoilHopNoteID()){
                        found = true;
                    }
                }
                if(!found){
                    doHopChangeCheck(im, en, 0, 0, bhn.getHopID().getHid(), bhn.getBoilHopLbs());
                }
            }
        } 
    }
    
    private void doHopChangeCheck(final InventoryModifiers im, final EntityManager en
            , final int newHopID, final float newHopQty
            , final int oldHopID, final float oldHopQty){
        if(newHopID != 0){
            if(oldHopID != newHopID && oldHopID != 0){
                //Credit old hop
                im.doHopReturnWithoutAllocation(en, oldHopID, oldHopQty);
                //Deduct new hop
                im.doHopRemovalWithAllocation(en, newHopID, newHopQty);
            } else if(oldHopQty != newHopQty){
                if(oldHopQty > newHopQty){
                    //Return old hop.
                    im.doHopReturnWithoutAllocation(en, oldHopID, (oldHopQty - newHopQty));
                } else {
                    //Deduct new hop.
                    im.doHopRemovalWithAllocation(en, newHopID, (newHopQty - oldHopQty));
                }
            }
        } else if(oldHopID != 0 && newHopID == 0){
            im.doHopReturnWithoutAllocation(en, oldHopID, oldHopQty);
        }
    }
    
    /***************************************************************************
     * Supply modifiers.
     * @param en
     * @param newBrewNote
     * @param oldBrewNote
     * @param recipe 
     ***************************************************************************/
    private void checkForSupplyChange(final EntityManager en, final BrewNotes newBrewNote
            , final BrewNotes oldBrewNote, final Recipes recipe, final InventoryModifiers im){
        List<AdditiveNotes> oldAdditiveNotes = new ArrayList<AdditiveNotes>();
        List<AdditiveNotes> newAdditiveNotes = new ArrayList<AdditiveNotes>();
        if(oldBrewNote.getAdditiveNoteCollection()!=null){
            oldAdditiveNotes = oldBrewNote.getAdditiveNoteCollection();
        }
        if(newBrewNote.getAdditiveNoteCollection()!=null){
            newAdditiveNotes = newBrewNote.getAdditiveNoteCollection();
        }
        if(oldAdditiveNotes.size() <= newAdditiveNotes.size()){
            int idx = 0;
            if(newBrewNote.getAdditiveNoteCollection()!=null){
                for(final AdditiveNotes saf : newBrewNote.getAdditiveNoteCollection()){
                    String units = saf.getSupply().getSupUnits();
                    if(recipe.getAdditiveCollection()!=null
                            && recipe.getAdditiveCollection().size()>idx){
                        if(saf.getSupply().getSupID() == recipe.getAdditiveCollection().get(idx).getSupply().getSupID()){
                            units = recipe.getAdditiveCollection().get(idx).getAdditiveUnits();
                        }
                    }
                    if(oldBrewNote.getAdditiveNoteCollection() != null
                            && oldBrewNote.getAdditiveNoteCollection().size() > idx){
                        final AdditiveNotes oldSaf = oldBrewNote.getAdditiveNoteCollection().get(idx);
                        doSupplyChangeCheck(im, en, saf.getSupply().getSupID(), saf.getAdditiveNoteLbs(),
                                oldSaf.getSupply().getSupID(), oldSaf.getAdditiveNoteLbs(), units);
                    } else {
                        doSupplyChangeCheck(im, en, saf.getSupply().getSupID(), saf.getAdditiveNoteLbs(),
                                0, 0, units);
                    }
                    idx++;
                }
            }
        } else {
            //One was removed we need to find it and credit it back.
            int idx = 0;
            for(final AdditiveNotes an : oldAdditiveNotes){
                boolean found = false;
                for(final AdditiveNotes newAn : newAdditiveNotes){
                    if(newAn.getAdditiveNoteID() == an.getAdditiveNoteID()){
                        found = true;
                    }
                }
                if(!found){
                    String units = an.getSupply().getSupUnits();
                    if(recipe.getAdditiveCollection()!=null
                            && recipe.getAdditiveCollection().size()>idx){
                        if(an.getSupply().getSupID() == recipe.getAdditiveCollection().get(idx).getSupply().getSupID()){
                            units = recipe.getAdditiveCollection().get(idx).getAdditiveUnits();
                        }
                    }
                    doSupplyChangeCheck(im, en, 0, 0, an.getSupply().getSupID(), an.getAdditiveNoteLbs(), units);
                }
                idx++;
            }
        } 
    }
    
    /***************************************************************************
     * Supply modifiers.
     * @param en
     * @param newBrewNote
     * @param oldBrewNote
     * @param recipe 
     ***************************************************************************/
    private void checkForWaterSaltChange(final EntityManager en, final BrewNotes newBrewNote
            , final BrewNotes oldBrewNote, final Recipes recipe, final InventoryModifiers im){
        List<SaltNotes> oldSaltNotes = new ArrayList<SaltNotes>();
        List<SaltNotes> newSaltNotes = new ArrayList<SaltNotes>();
        if(oldBrewNote.getSaltNoteCollection()!=null){
            oldSaltNotes = oldBrewNote.getSaltNoteCollection();
        }
        if(newBrewNote.getSaltNoteCollection()!=null){
            newSaltNotes = newBrewNote.getSaltNoteCollection();
        }
        if(oldSaltNotes.size() <= newSaltNotes.size()){
            int idx = 0;
            if(newBrewNote.getSaltNoteCollection()!=null){
                for(final SaltNotes saltNote : newBrewNote.getSaltNoteCollection()){
                    String units = Units.getUnitShortDescription(Units.MASS_G.getUnit());
                    if(oldBrewNote.getSaltNoteCollection() != null
                            && oldBrewNote.getSaltNoteCollection().size() > idx){
                        final SaltNotes oldSaltNote = oldBrewNote.getSaltNoteCollection().get(idx);
                        if(oldSaltNote.getWaterSaltID().getSupID()!=null && saltNote.getWaterSaltID().getSupID()!=null){
                            doSupplyChangeCheck(im, en, saltNote.getWaterSaltID().getSupID().getSupID(), saltNote.getSaltGrams(),
                                    oldSaltNote.getWaterSaltID().getSupID().getSupID(), oldSaltNote.getSaltGrams(), units);
                        }
                    } else {
                        if(saltNote.getWaterSaltID().getSupID()!=null){
                            doSupplyChangeCheck(im, en, saltNote.getWaterSaltID().getSupID().getSupID(), saltNote.getSaltGrams(),
                                    0, 0, units);
                        }
                    }
                    idx++;
                }
            }
        } else {
            //One was removed we need to find it and credit it back.
            int idx = 0;
            for(final SaltNotes sn : oldSaltNotes){
                boolean found = false;
                for(final SaltNotes newSn : newSaltNotes){
                    if(newSn.getSaltNoteID() == sn.getSaltNoteID()){
                        found = true;
                    }
                }
                if(!found){
                    String units = Units.getUnitShortDescription(Units.MASS_G.getUnit());
                    doSupplyChangeCheck(im, en, 0, 0, sn.getWaterSaltID().getSupID().getSupID(), sn.getSaltGrams(), units);
                }
                idx++;
            }
        } 
    }
    
    private void doSupplyChangeCheck(final InventoryModifiers im, final EntityManager en
            , final int newSupplyID, final float newSupplyQty
            , final int oldSupplyID, final float oldSupplyQty
            , final String recipeUnits){
        if(newSupplyID != 0){
            if(oldSupplyID!=newSupplyID && oldSupplyID!=0){
                float oldlbs = getConvertedWeight(en, recipeUnits, oldSupplyID, oldSupplyQty);
                im.doSupplyReturnWithoutAllocation(en, oldSupplyID, oldlbs);
                //Now increase new supply.
                final float newlbs = getConvertedWeight(en, recipeUnits, newSupplyID, newSupplyQty);
                im.doSupplyRemovalWithAllocation(en, newSupplyID, newlbs);
            } else if(oldSupplyQty!=newSupplyQty){
                if(oldSupplyQty > newSupplyQty){
                    //return difference of supply to stock.
                    final float returnLbs = getConvertedWeight(en, recipeUnits, oldSupplyID, (oldSupplyQty - newSupplyQty));
                    im.doSupplyReturnWithoutAllocation(en, oldSupplyID, returnLbs);
                } else {
                    //remove difference of supply from stock.
                    final float removeLbs = getConvertedWeight(en, recipeUnits, newSupplyID, (newSupplyQty - oldSupplyQty));
                    im.doSupplyRemovalWithAllocation(en, newSupplyID, removeLbs);
                }
            } 
        } else if(oldSupplyID != 0 && newSupplyID == 0){
            final float returnLbs = getConvertedWeight(en, recipeUnits, oldSupplyID, oldSupplyQty);
            im.doSupplyReturnWithoutAllocation(en, oldSupplyID, returnLbs);
        }
    }
    
    private float getConvertedWeight(final EntityManager en, final String recipeUnits,
            final int supplyID, final float supplyQty){
        float returnVal = 0;
        if(supplyID != 0){
            final Supplies sup = getSuppliesFromID(en, supplyID);
            final int unitFrom = Units.getUnitFromShortDescription(recipeUnits);
            final int unitTo = Units.getUnitFromShortDescription(sup.getSupUnits());
            returnVal = Units.convertUnits(unitFrom, unitTo, supplyQty);
        }
        return returnVal;
    }
    
    /***************************************************************************
     * Database access Functions.
     **************************************************************************/
    private BrewNotes getBrewNoteFromID(final EntityManager en, final int brewNoteID){
        TypedQuery<BrewNotes> query = en.createQuery(
                "SELECT b FROM BrewNotes b WHERE b.bnid = ?1", BrewNotes.class);
        query.setParameter(1, brewNoteID);
        BrewNotes updatedBrewNote = query.getSingleResult();
        return updatedBrewNote;
    }
    
    private Supplies getSuppliesFromID(final EntityManager en, final int supplyID){
        TypedQuery<Supplies> query = en.createQuery(
                "SELECT s FROM Supplies s WHERE s.supID = ?1", Supplies.class);
        query.setParameter(1, supplyID);
        Supplies supply = query.getSingleResult();
        return supply;
    }
    
    private Hops getHopsFromID(final EntityManager en, final int hopID){
        TypedQuery<Hops> query = en.createQuery(
                "SELECT h FROM Hops h WHERE h.hid = ?1", Hops.class);
        query.setParameter(1, hopID);
        Hops hop = query.getSingleResult();
        return hop;
    }
    
    private Beer getBeerFromID(final EntityManager en, final int beerID){
        TypedQuery<Beer> query = en.createQuery(
                "SELECT b FROM Beer b WHERE b.bId = ?1", Beer.class);
        query.setParameter(1, beerID);
        Beer beer = query.getSingleResult();
        return beer;
    }
    
    private Administration getAdmin(final EntityManager en){
        TypedQuery<Administration> query = en.createQuery(
            "SELECT a FROM Administration a WHERE a.aNum = '1'", Administration.class);
        Administration admin = query.getSingleResult();
        return admin;
    }
    
    private Brews getBrewFromID(final EntityManager en, final int batchID){
        TypedQuery<Brews> query = en.createQuery(
                "SELECT b FROM Brews b WHERE b.bid = ?1", Brews.class);
        query.setParameter(1, batchID);
        Brews updatedBatch = query.getSingleResult();
        return updatedBatch;
    }
    
    private BrewView getBrewViewFromID(final EntityManager en, final int batchID){
        TypedQuery<BrewView> query = en.createQuery(
                "SELECT b FROM BrewView b WHERE b.bid = ?1", BrewView.class);
        query.setParameter(1, batchID);
        BrewView updatedBatch = query.getSingleResult();
        return updatedBatch;
    }
    
    public BrewView doCreateNewBrewSchedule(final EntityManager en, final int beerID, final float volume, final Date brewDate){
        BeerView bv = getBeerViewFromID(en, beerID);
        Administration admin = getAdmin(en);
        final Recipes recipe = getRecipeFromID(en, bv.getRecipeID());
        
        //Determine number of brew notes to create for batch.
        int numnotes = 1;
        final double batchsize = volume;
        final double numbatches = batchsize / admin.getSystemSize();
        final double num = Math.ceil(numbatches);
        numnotes = (int) num;
        BrewNotes bn1 = null;
        BrewNotes bn2 = null;
        BrewNotes bn3 = null;
        BrewNotes bn4 = null;
        BrewNotes bn5 = null;
        BrewNotes bn6 = null;

        //Add brew Notes.
        for (int x = 0; x < numnotes; x++) {
            //insert a new brew note for every item.
            if(x==0){
                bn1 = createNewEmptyBrewNote();
            } else if(x==1){
                bn2 = createNewEmptyBrewNote();
            } else if(x==2){
                bn3 = createNewEmptyBrewNote();
            } else if(x==3){
                bn4 = createNewEmptyBrewNote();
            } else if(x==4){
                bn5 = createNewEmptyBrewNote();
            } else if(x==5){
                bn6 = createNewEmptyBrewNote();
            }
        }
        //Add brew
        final Brews insertBrew = createNewBrew(bv, recipe
                , brewDate, bn1, bn2, bn3, bn4, bn5, bn6
                , MovementState.BREWING.getState(), brewDate);
        Brews brew = doCreateNewBrew(en, insertBrew, volume);
        
        //en.merge(brew);
        en.getEntityManagerFactory().getCache().evict(BrewView.class);
        en.getEntityManagerFactory().getCache().evict(Brews.class);
        
        return getBrewViewFromID(en, (int)brew.getBid());
    }
    
    private boolean vesselPrimaryOpen(Date startDate, Date stopDate, final int vID, final List<BrewView> brewItems){
        boolean open = true;
        Calendar c = Calendar.getInstance();
        c.setTime(startDate);
        Date date = startDate;
        while((date.before(stopDate))){
            for(final BrewView brewItem : brewItems){
                if(brewItem.getbState()== MovementState.BREWING.getState()){
                    if(brewItem.getGanttPID() == vID){
                        if(((date.after(brewItem.getGanttStart()))&&(date.before(brewItem.getGanttSecond())))
                                || date.equals(brewItem.getGanttStart())){
                            open = false;
                        }
                    }
                    if(brewItem.getGanttSID() == vID){
                        if(((date.after(brewItem.getGanttCond()))
                                &&(date.before(brewItem.getGanttCond())))
                                || date.equals(brewItem.getGanttCond())){
                            open = false;
                        }
                    }
                }
                if(brewItem.getbState() == MovementState.FERMENTING.getState()){
                    if(brewItem.getPfvid()== vID){
                        if(((date.after(brewItem.getbDate()))&&(date.before(brewItem.getGanttSecond())))
                                || date.equals(brewItem.getbDate())){
                            open = false;
                        }
                    }
                    if(brewItem.getGanttSID() == vID){
                        if(((date.after(brewItem.getGanttSecond()))
                                &&(date.before(brewItem.getGanttCond())))
                                || date.equals(brewItem.getGanttSecond())){
                            open = false;
                        }
                    }
                }
                if(brewItem.getbState() == MovementState.LAGERING.getState()){
                    if(brewItem.getPfvid() == vID){
                        if(((date.after(brewItem.getbDate()))
                                &&(date.before(brewItem.getsFDate01())))
                                || date.equals(brewItem.getbDate())){
                            open = false;
                        }
                    }
                    if(brewItem.getSfvid()== vID){
                        if(((date.after(brewItem.getsFDate01()))
                                &&(date.before(brewItem.getGanttCond())))
                                || date.equals(brewItem.getsFDate01())){
                            open = false;
                        }
                    }
                }
            }
            c.add(Calendar.DATE, 1);
            date = new java.util.Date(c.getTime().getTime());
        }
        return open;
    }
    
    private boolean vesselBriteOpen(Date startDate, Date stopDate, final int vID, final List<BrewView> brewItems){
        boolean open = true;
        Calendar c = Calendar.getInstance();
        c.setTime(startDate);
        Date date = startDate;
        while((date.before(stopDate)) || (date.equals(stopDate))){
            for(final BrewView brewItem : brewItems){
                if(brewItem.getbState() < MovementState.CONDITIONING.getState()){
                    if(brewItem.getGanttCID()== vID){
                        if(((date.after(brewItem.getGanttCond()))
                                &&(date.before(brewItem.getGanttStop())))
                                || date.equals(brewItem.getGanttCond())){
                            open = false;
                        }
                    }
                    if(brewItem.getGanttSID()== vID){
                        if(((date.after(brewItem.getGanttSecond()))
                                &&(date.before(brewItem.getGanttCond())))
                                || date.equals(brewItem.getGanttSecond())){
                            open = false;
                        }
                    }
                }
                if(brewItem.getbState() == MovementState.CONDITIONING.getState()){
                    if(brewItem.getCvid()== vID){
                        if(((date.after(brewItem.getcTDate()))&&(date.before(brewItem.getGanttStop())))
                                || date.equals(brewItem.getGanttStop())){
                            open = false;
                        }
                    }
                }
            }
            c.add(Calendar.DATE, 1);
            date = new java.util.Date(c.getTime().getTime());
        }
        return open;
    }
    
    public BrewNotes createNewEmptyBrewNote(){
        BrewNotes bn = new BrewNotes();
        try{
            bn.setEmployeeID(0);
            bn.setBoilStart(timeFormat.parse("00:00"));
            bn.setBoilStop(timeFormat.parse("00:00"));
            bn.setBoilpH(0);
            bn.setVolFinal(0);
            bn.setFlowrate(0);
            bn.setHydStart(timeFormat.parse("00:00"));
            bn.setHydStop(timeFormat.parse("00:00"));
            bn.setHydTemp(0);
            bn.setKOStop(timeFormat.parse("00:00"));
            bn.setKOTemp(0);
            List<MaltNotes> mns = new ArrayList<MaltNotes>();
            bn.setMaltNoteCollection(mns);
            bn.setMashpH(0);
            bn.setBog(0);
            bn.setBOxy(0);
            bn.setPBBrix(0);
            bn.setPBVol(0);
            bn.setRestStop(timeFormat.parse("00:00"));
            bn.setSpargeBrix(0);
            bn.setSpargepH(0);
            bn.setVorNotes("New note inserted by system");
            bn.setVorStart(timeFormat.parse("00:00"));
            bn.setVorStop(timeFormat.parse("00:00"));
            bn.setWhirlStop(timeFormat.parse("00:00"));
            return bn;
        }   catch(ParseException e){
            final String msg = "Error creating new Brew Note Record, retry!";
            JOptionPane.showMessageDialog(null, msg, "ERROR!", JOptionPane.ERROR_MESSAGE);
        }
        return null;
    }
    
    /***************************************************************************
     * Do an update to brew based on before and after update.
     * @param en - Entity Manager
     * @param brew - brew being added including brewNotes
     * @param volume
     * @return brew to return to user.
     **************************************************************************/
    public Brews doCreateNewBrew(final EntityManager en, final Brews brew, final float volume){
        int numberOfNotes = 0;
        if(brew.getBrewNoteID()!=null){
            final BrewNotes bn = createNewBrewNote(en, brew.getBrewNoteID(), brew.getRid());
            brew.setBrewNoteID(bn);
            numberOfNotes++;
        }
        if(brew.getBrewNoteID2()!=null){
            final BrewNotes bn = createNewBrewNote(en, brew.getBrewNoteID2(), brew.getRid());
            brew.setBrewNoteID2(bn);
            numberOfNotes++;
        }
        if(brew.getBrewNoteID3()!=null){
            final BrewNotes bn = createNewBrewNote(en, brew.getBrewNoteID3(), brew.getRid());
            brew.setBrewNoteID3(bn);
            numberOfNotes++;
        }
        if(brew.getBrewNoteID4()!=null){
            final BrewNotes bn = createNewBrewNote(en, brew.getBrewNoteID4(), brew.getRid());
            brew.setBrewNoteID4(bn);
            numberOfNotes++;
        }
        if(brew.getBrewNoteID5()!=null){
            final BrewNotes bn = createNewBrewNote(en, brew.getBrewNoteID5(), brew.getRid());
            brew.setBrewNoteID5(bn);
            numberOfNotes++;
        }
        if(brew.getBrewNoteID6()!=null){
            final BrewNotes bn = createNewBrewNote(en, brew.getBrewNoteID6(), brew.getRid());
            brew.setBrewNoteID6(bn);
            numberOfNotes++;
        }
        brew.setDryHopNoteCollection(createDryHopNotes(en, brew.getRid()));
        
        Calendar c = Calendar.getInstance();
        c.setTime(brew.getGanttStart());
        
        List<Fv> vesselItems = getAllVessels(en);
        List<BrewView> brews = getAllOpenBrewViews(en);
        
        Beer beer = getBeerFromID(en, brew.getBBeerID().getbId());
        
        int pDays = beer.getBeerFermDays();
        int sDays = beer.getBeerSecondDays();
        int cDays = beer.getBeerCondDays();
        boolean fine = beer.getBeerFine();
        boolean filter = beer.getBeerFilter();
        
        //If days are zero set defaults.
        if(pDays==0){
            pDays = 10;
        }
        
        if(sDays==0){
            sDays = 10;
        }
        
        if(cDays==0){
            cDays = 1;
        }
        
        c.add(Calendar.DATE, pDays);
        Date lageringDate = new Date (c.getTime().getTime());
        c.add(Calendar.DATE, sDays);
        Date conditioningDate = new Date (c.getTime().getTime());
        c.add(Calendar.DATE, cDays);
        Date packagingDate = new Date (c.getTime().getTime());
        
        int pID = 0;
        int sID = 0;
        int cID = 0;
        
        //Determine which vessels can be used.
        for(final Fv ves : vesselItems){
            if((ves.getFVRate() >= (float)volume)&&(ves.getFvprm())){
                if(vesselPrimaryOpen(brew.getGanttStart(),lageringDate,ves.getFvid(), brews)
                        &&vesselPrimaryOpen(lageringDate,conditioningDate,ves.getFvid(), brews)){
                    pID = ves.getFvid();
                    sID = ves.getFvid();
                }
            }
            if((pID!=0)&&(sID!=0)){
                break;
            }
        }
        
        //Determine which vessels can be used.
        for(final Fv ves : vesselItems){
            if((ves.getFVRate() >= (float)volume)&&(!ves.getFvprm())){
                if(vesselBriteOpen(conditioningDate,packagingDate,ves.getFvid(), brews)){
                    cID = ves.getFvid();
                }
            }
            if(cID!=0){
                break;
            }
        }
        
        if((pID!=0)||(sID!=0)||(cID!=0)){
            brew.setGanttSecond(lageringDate);
            brew.setGanttCond(conditioningDate);
            brew.setGanttStop(packagingDate);
            brew.setGanttPID(pID);
            brew.setGanttSID(sID);
            brew.setGanttCID(cID);
            brew.setGanttFilter(filter);
            brew.setGanttFine(fine);
        }
        
        en.persist(brew);
        en.flush();
        en.refresh(brew);
        fixAllAllocations(en);
        createBrewLabs(en, brew, numberOfNotes);
        return brew;
    }
    
    private BrewNotes createNewBrewNote(final EntityManager en, final BrewNotes bn, final Recipes recipe){
        bn.setMaltNoteCollection(createMaltNotes(en, recipe));
        bn.setMashNoteCollection(createMashNotes(en, recipe));
        bn.setBoilHopNoteCollection(createBoilHopNotes(en, recipe));
        bn.setAdditiveNoteCollection(createAdditiveNotes(en, recipe));
        bn.setSaltNoteCollection(createSaltNotes(en, recipe));
        en.persist(bn);
        en.flush();
        en.refresh(bn);
        return bn;
    }
    
    private List<MaltNotes> createMaltNotes(final EntityManager en, final Recipes recipe){
        List<MaltNotes> newMaltNotes = new ArrayList<MaltNotes>();
        if(recipe.getMaltBillCollection()!=null){
            for(final MaltBill mb : recipe.getMaltBillCollection()){
                newMaltNotes.add(createNewMaltNote(en, mb));
            }
        }
        return newMaltNotes;
    }
    
    private List<MashNotes> createMashNotes(final EntityManager en, final Recipes recipe){
        List<MashNotes> newMashNotes = new ArrayList<MashNotes>();
        if(recipe.getMashSchedule().getMashStepCollection()!=null){
            Collections.sort(recipe.getMashSchedule().getMashStepCollection(), new Comparator<MashStep>(){
                @Override
                public int compare(MashStep h1, MashStep h2){
                    if(h1.getTemperature()>h2.getTemperature()) return 1;
                    if(h1.getTemperature()<h2.getTemperature()) return -1;
                    return 0;
                }    
            });
            
            for(final MashStep ms : recipe.getMashSchedule().getMashStepCollection()){
                newMashNotes.add(createNewMashNote(en, ms));
            }
        }
        return newMashNotes;
    }
    
    private List<BoilHopNotes> createBoilHopNotes(final EntityManager en, final Recipes recipe){
        List<BoilHopNotes> newBoilHopNotes = new ArrayList<BoilHopNotes>();
        if(recipe.getBoilHopCollection()!=null){
            Collections.sort(recipe.getBoilHopCollection(), new Comparator<BoilHops>(){
                @Override
                public int compare(BoilHops h1, BoilHops h2){
                    if(h1.getBoilHopMinutes()>h2.getBoilHopMinutes()) return -1;
                    if(h1.getBoilHopMinutes()<h2.getBoilHopMinutes()) return 1;
                    return 0;
                }    
            });
            for(final BoilHops bh : recipe.getBoilHopCollection()){
                newBoilHopNotes.add(createBoilHopNote(en, bh));
            }
        }
        return newBoilHopNotes;
    }
    
    private List<AdditiveNotes> createAdditiveNotes(final EntityManager en, final Recipes recipe){
        List<AdditiveNotes> newAdditiveNotes = new ArrayList<AdditiveNotes>();
        if(recipe.getAdditiveCollection()!=null){
            for(final RecipeAdditives ra : recipe.getAdditiveCollection()){
                newAdditiveNotes.add(createAdditiveNote(en, ra));
            }
        }
        return newAdditiveNotes;
    }
    
    private List<SaltNotes> createSaltNotes(final EntityManager en, final Recipes recipe){
        List<SaltNotes> newSaltNotes = new ArrayList<SaltNotes>();
        if(recipe.getRecipeSaltID()!=null && recipe.getRecipeSaltID().getSelectedSaltCollection()!=null){
            for(final SelectedSalt ss : recipe.getRecipeSaltID().getSelectedSaltCollection()){
                newSaltNotes.add(createSaltNote(en, ss));
            }
        }
        return newSaltNotes;
    }
    
    private List<DryHopNotes> createDryHopNotes(final EntityManager en, final Recipes recipe){
        List<DryHopNotes> newDryHopNotes = new ArrayList<DryHopNotes>();
        if(recipe.getDryHopCollection()!=null){
            for(final DryHops dh : recipe.getDryHopCollection()){
                newDryHopNotes.add(createDryHopNote(en, dh));
            }
        }
        return newDryHopNotes;
    }
    
    private MaltNotes createNewMaltNote(final EntityManager en, final MaltBill mb){
        MaltNotes mn = new MaltNotes();
        mn.setMaltID(mb.getMaltID());
        mn.setMaltNoteLbs(0);
        en.persist(mn);
        en.flush();
        en.refresh(mn);
        return mn;
    }
    
    private MashNotes createNewMashNote(final EntityManager en, final MashStep ms){
        DateFormat timeFormat = new SimpleDateFormat("HH:mm");
        try{
            MashNotes mn = new MashNotes();
            mn.setMashType(ms.getMashType());
            mn.setNotes("");
            mn.setStopTime(timeFormat.parse("00:00"));
            mn.setTemp(0);
            en.persist(mn);
            en.flush();
            en.refresh(mn);
            return mn;
        } catch(ParseException e){
        }
        return null;
    }
    
    private BoilHopNotes createBoilHopNote(final EntityManager en, final BoilHops bh){
        DateFormat timeFormat = new SimpleDateFormat("HH:mm");
        try{
            BoilHopNotes bhn = new BoilHopNotes();
            bhn.setBoilHopLbs(0);
            bhn.setHopID(bh.getHopID());
            bhn.setBoilHopTime(timeFormat.parse("00:00"));
            en.persist(bhn);
            en.flush();
            en.refresh(bhn);
            return bhn;
        } catch(ParseException e){
        }
        return null;
    }
    
    private AdditiveNotes createAdditiveNote(final EntityManager en, final RecipeAdditives ra){
        AdditiveNotes an = new AdditiveNotes();
        an.setAdditiveNoteLbs(0);
        an.setAdditiveNoteNotes("");
        an.setSupply(ra.getSupply());
        en.persist(an);
        en.flush();
        en.refresh(an);
        return an;
    }
    
    private DryHopNotes createDryHopNote(final EntityManager en, final DryHops dh){
        DryHopNotes dhn = new DryHopNotes();
        dhn.setDryHopLbs(0);
        dhn.setDryHopStart(null);
        dhn.setDryHopStop(null);
        dhn.setHopID(dh.getHopID());
        en.persist(dhn);
        en.flush();
        en.refresh(dhn);
        return dhn;
    }
    
    private SaltNotes createSaltNote(final EntityManager en, final SelectedSalt ss){
        SaltNotes sn = new SaltNotes();
        sn.setSaltGrams(0);
        sn.setSaltNotes("");
        sn.setWaterSaltID(ss.getWaterSaltID());
        en.persist(sn);
        en.flush();
        en.refresh(sn);
        return sn;
    }
    
    private void fixAllAllocations(final EntityManager en){
        InventoryModifiers im = new InventoryModifiers();
        im.runHopAllocationFix(en);
        im.runFGIAllocationFix(en);
        im.runMaltAllocationFix(en);
        im.runSupplyAllocationFix(en);
    }
    
    private void createBrewLabs(final EntityManager en, final Brews brew, final int numberOfNotes){
        final Administration admin = getAdmin(en);
        if(admin.getLabTypeCollection()!=null){
            List<BrewLabDetails> blds = new ArrayList<BrewLabDetails>();
            for(final LabType lt : admin.getLabTypeCollection()){
                if(lt.isLtPerBrew()){
                    //Create a brew note per the number of notes needed.
                    for(int x=0; x<numberOfNotes; x++){
                        blds.add(makeBrewLabDetail(en, lt, brew));
                    }
                } else {
                    blds.add(makeBrewLabDetail(en, lt, brew));
                }
            }
            final String desc = "Brew: " + Integer.toString(brew.getBid()) + " (" + brew.getBBeerID().getbName() + ") - Lab Notes";
            final BrewView bv = getBrewViewFromID(en, brew.getBid());
            BrewLab bl = new BrewLab();
            bl.setBrews(bv);
            bl.setBrewLabDetailsCollection(blds);
            bl.setBlComplete(false);
            bl.setBldesc(desc);
            bl.setBlNotes("");
            en.persist(bl);
        }
    }
    
    private BrewLabDetails makeBrewLabDetail(final EntityManager en, final LabType lt, final Brews brew){
        BrewLabDetails bld = new BrewLabDetails();
        final String desc = lt.getLtdesc() + " - Brew: " + Integer.toString(brew.getBid());
        bld.setBlddesc(desc);
        bld.setLabType(lt);
        bld.setBldNotes("");
        bld.setBldResult(LabResult.UNCOMPLETED.getResult());
        bld.setDate(brew.getBDate());
        en.persist(bld);
        en.flush();
        en.refresh(bld);
        return bld;
    }
    
    /***************************************************************************
     * Do an update to brew and refactor if required.
     * @param en - Entity Manager
     * @param brew - brew being added including brewNotes
     **************************************************************************/
    public void doUpdateAndCheckForRefactor(final EntityManager en, final BrewView brew){
        final BrewView oldBrew = getBrewViewFromID(en, brew.getBid());
        boolean recipeRefactorRequired = false;
        boolean fgiRefactorRequired = false;
        if(oldBrew.getbBeerID().getbId() != brew.getbBeerID().getbId()){
            recipeRefactorRequired = true;
        }
        if(!oldBrew.getGanttFGI().equals(brew.getGanttFGI())){
            fgiRefactorRequired = true;
        }
        en.merge(brew);
        if(recipeRefactorRequired){
            final Beer beer = getBeerFromID(en, brew.getbBeerID().getbId());
            final Brews updateBrew = getBrewFromID(en, brew.getBid());
            updateBrew.setRid(beer.getRecipeID());
            en.merge(updateBrew);
            fixAllAllocations(en);
        } else if(fgiRefactorRequired){
            fixAllAllocations(en);
        }
        en.getEntityManagerFactory().getCache().evictAll();
    }
    
    /***************************************************************************
     * Code to perform batch splits.
     **************************************************************************/
    /***************************************************************************
     * Perform a batch split
     * @param en - Entity Manager
     * @param brew - brew being added including brewNotes
     * @param vesselID
     * @param beerID
     * @param volume
     * @return 
     **************************************************************************/
    public Brews doBatchSplit(final EntityManager en, Brews brew, final int vesselID, final int beerID, final float volume) {
        final Fv vessel = getVesselFromID(en, vesselID);
        final BeerView beer = getBeerViewFromID(en, beerID);
        if (volume <= vessel.getFVCap()) {
            int numNoteToMove = getNumNotes(volume, en);

            Brews newBrew = copyBrew(brew);
            newBrew.setBrewNoteID(null);
            newBrew.setBrewNoteID2(null);
            newBrew.setBrewNoteID3(null);
            newBrew.setBrewNoteID4(null);
            newBrew.setBrewNoteID5(null);
            newBrew.setBrewNoteID6(null);
            newBrew.setVolLeft(volume); //Set new notes volume to that moved.
            newBrew.setBBeerID(beer);
            final Recipes recipe = brew.getRid();
            newBrew.setRid(recipe);
            newBrew.setOrigBrew(brew.getBid());
            if (newBrew.getSFVol01() != 0) {
                newBrew.setSFVol01(volume);
            }
            if (newBrew.getCTVol() != 0) {
                newBrew.setCTVol(volume);
            }

            //Set new vessel it is going to.
            if (newBrew.getBState() <= MovementState.FERMENTING.getState()) {
                newBrew.setPfvid(vessel.getFvid());
            } else if (newBrew.getBState() == MovementState.LAGERING.getState()) {
                newBrew.setSfvid(vessel.getFvid());
            } else if (newBrew.getBState() == MovementState.CONDITIONING.getState()) {
                newBrew.setCvid(vessel.getFvid());
            }

            while (numNoteToMove > 0) {
                BrewNotes bnid = getLastBrewNote(brew);
                //Set new brew note ids.
                if (numNoteToMove == 6) {
                    newBrew.setBrewNoteID6(bnid);
                } else if (numNoteToMove == 5) {
                    newBrew.setBrewNoteID5(bnid);
                } else if (numNoteToMove == 4) {
                    newBrew.setBrewNoteID4(bnid);
                } else if (numNoteToMove == 3) {
                    newBrew.setBrewNoteID3(bnid);
                } else if (numNoteToMove == 2) {
                    newBrew.setBrewNoteID2(bnid);
                } else if (numNoteToMove == 1) {
                    newBrew.setBrewNoteID(bnid);
                }

                //Set old brew note to 0;
                if (brew.getBrewNoteID().getBnid() == bnid.getBnid()) {
                    brew.setBrewNoteID(null);
                } else if (brew.getBrewNoteID2().getBnid() == bnid.getBnid()) {
                    brew.setBrewNoteID2(null);
                } else if (brew.getBrewNoteID3().getBnid() == bnid.getBnid()) {
                    brew.setBrewNoteID3(null);
                } else if (brew.getBrewNoteID4().getBnid() == bnid.getBnid()) {
                    brew.setBrewNoteID4(null);
                } else if (brew.getBrewNoteID5().getBnid() == bnid.getBnid()) {
                    brew.setBrewNoteID5(null);
                } else if (brew.getBrewNoteID6().getBnid() == bnid.getBnid()) {
                    brew.setBrewNoteID6(null);
                }

                numNoteToMove--;
            }
            
            //insertNewBrew
            en.persist(newBrew);

            //Update old brew
            float volLeft = brew.getVolLeft() - volume;
            brew.setVolLeft(volLeft);
                    //brew.setBrewNoteID(null);
            if (brew.getSFVol01() != 0) {
                float nvolume = brew.getSFVol01() - volume;
                brew.setSFVol01(nvolume);
            }
            if (brew.getCTVol() != 0) {
                float nvolume = brew.getCTVol() - volume;
                brew.setCTVol(nvolume);
            }
            en.merge(brew);
            en.getEntityManagerFactory().getCache().evictAll();
        }
        
        return brew;
    }
    
    private BrewNotes getLastBrewNote(final Brews brew){
        if(brew.getBrewNoteID6() != null){
            return brew.getBrewNoteID6();
        }
        if(brew.getBrewNoteID5() != null){
            return brew.getBrewNoteID5();
        }
        if(brew.getBrewNoteID4() != null){
            return brew.getBrewNoteID4();
        }
        if(brew.getBrewNoteID3() != null){
            return brew.getBrewNoteID3();
        }
        if(brew.getBrewNoteID2() != null){
            return brew.getBrewNoteID2();
        }
        if(brew.getBrewNoteID() != null){
            return brew.getBrewNoteID();
        }
        return null;
    }
    
    private int getNumNotes(final float volume, final EntityManager en){
        int retVal = 1;
        final Administration admin = getAdmin(en);
        double volLeft = (double)volume - admin.getSystemSize();
        while(volLeft > 0){
            //Check to see if we need to move more than the default notes.
            if(volLeft > admin.getSystemSize() - 3){
                retVal++;
                volLeft = volLeft - admin.getSystemSize();
            } else {
                volLeft = volLeft - admin.getSystemSize();
            }
        }
        
        return retVal;
    }
    
    private Brews copyBrew(final Brews brew){
        Brews b = createNewBrew(brew.getBBeerID(), brew.getRid(), brew.getBDate()
                , null, null, null, null, null, null, brew.getBState(), brew.getGanttStart());
        b.setBrewNoteID(null);
        b.setBrewNoteID2(null);
        b.setBrewNoteID3(null);
        b.setBrewNoteID4(null);
        b.setBrewNoteID5(null);
        b.setBrewNoteID6(null);
        b.setRid(brew.getRid());
        b.setBBeerID(brew.getBBeerID());
        b.setBState(brew.getBState());
        b.setBDate(brew.getBDate());
        b.setFermentationNoteCollection(brew.getFermentationNoteCollection());
        b.setSFNote01(brew.getSFNote01());
        b.setSFDate01(brew.getSFDate01());
        b.setSFGrav01(brew.getSFGrav01());
        b.setSFTemp01(brew.getSFTemp01());
        b.setSFTime01(brew.getSFTime01());
        b.setSFVol01(brew.getSFVol01());
        b.setSfvid(brew.getSfvid());
        b.setBtg(brew.getBtg());
        b.setCTDate(brew.getCTDate());
        b.setCTTime(brew.getCTTime());
        b.setBpsi(brew.getBpsi());
        b.setCTTemp(brew.getCTTemp());
        b.setPDate(brew.getPDate());
        b.setPTime(brew.getPTime());
        b.setVolLeft(brew.getVolLeft());
        b.setCTVol(brew.getCTVol());
        b.setDryHopNoteCollection(brew.getDryHopNoteCollection());
        b.setPfvid(brew.getPfvid());
        b.setCvid(brew.getCvid());
        b.setGanttColor(brew.getGanttColor());
        b.setGanttStart(brew.getGanttStart());
        b.setGanttSecond(brew.getGanttSecond());
        b.setGanttCond(brew.getGanttCond());
        b.setGanttStop(brew.getGanttStop());
        b.setGanttPID(brew.getGanttPID());
        b.setGanttSID(brew.getGanttSID());
        b.setCvid(brew.getCvid());
        b.setGanttFilter(brew.getGanttFilter());
        b.setGanttHarvest(brew.getGanttHarvest());
        b.setGanttHarvestDate(brew.getGanttHarvestDate());
        b.setGanttFine(brew.getGanttFine());
        b.setPitchNoteCollection(brew.getPitchNoteCollection());
        return b;
    }
    
    public Brews createNewBrew(final BeerView beer, final Recipes recipe
            , java.util.Date brewDate, final BrewNotes id1, final BrewNotes id2
            , final BrewNotes id3, final BrewNotes id4, final BrewNotes id5, final BrewNotes id6
            , final int state, final java.util.Date ganttStart){
        Brews b = new Brews();
        try{
            b.setBBeerID(beer);
            b.setBDate(brewDate);
            b.setBrewNoteID(id1);
            b.setBrewNoteID2(id2);
            b.setBrewNoteID3(id3);
            b.setBrewNoteID4(id4);
            b.setBrewNoteID5(id5);
            b.setBrewNoteID6(id6);
            b.setBpsi(0);
            b.setCTDate(null);
            b.setCTVol(0);
            b.setCTTemp(0);
            b.setCTTime(timeFormat.parse("00:00"));
            b.setCvid(0);
            b.setGanttColor(0);
            b.setGanttCID(0);
            b.setGanttFGI("");
            b.setGanttFilter(false);
            b.setGanttFine(false);
            b.setGanttHarvest(false);
            b.setGanttPID(0);
            b.setGanttSID(0);
            b.setOrigBrew(0);
            b.setPDate(null);
            b.setPTime(timeFormat.parse("00:00"));
            b.setPfvid(0);
            b.setRid(recipe);
            b.setSFDate01(null);
            b.setSFGrav01(0);
            b.setSFNote01("");
            b.setSFTemp01(0);
            b.setSFTime01(timeFormat.parse("00:00"));
            b.setSFVol01(0);
            b.setSfvid(0);
            b.setBState(state);
            b.setBtg(0);
            b.setVersion(0);
            b.setVolLeft(0);
            b.setGanttStart(ganttStart);
            b.setGanttCond(null);
            b.setGanttHarvestDate(null);
            b.setGanttSecond(null);
            b.setGanttStop(null);
            return b;
        } catch(ParseException e){
            final String msg = "Error creating new Brew Note Record, retry!";
            JOptionPane.showMessageDialog(null, msg, "ERROR!", JOptionPane.ERROR_MESSAGE);
        }
        return null;
    }
    
    private Fv getVesselFromID(final EntityManager en, final int veselID){
        TypedQuery<Fv> query = en.createQuery(
                "SELECT f FROM Fv f WHERE f.fvid = ?1", Fv.class);
        query.setParameter(1, veselID);
        return query.getSingleResult();
    }
    
    private BeerView getBeerViewFromID(final EntityManager en, final int beerID){
        TypedQuery<BeerView> query = en.createQuery(
                "SELECT b FROM BeerView b WHERE b.bId = ?1", BeerView.class);
        query.setParameter(1, beerID);
        return query.getSingleResult();
    }
    
    private Recipes getRecipeFromID(final EntityManager en, final int recipeID){
        TypedQuery<Recipes> query = en.createQuery("SELECT r FROM Recipes r WHERE r.rid = ?1", Recipes.class);
        query.setParameter(1, recipeID);
        return query.getSingleResult();
    }
    
    private List<Fv> getAllVessels(final EntityManager en){
        TypedQuery<Fv> query = en.createNamedQuery("Fv.findAll", Fv.class);
        return query.getResultList();
    }
    
    private List<BrewView> getAllOpenBrewViews(final EntityManager en){
        TypedQuery<BrewView> query = en.createNamedQuery("BrewView.findAllInProduction", BrewView.class);
        return query.getResultList();
    }
}
